/**
 * 监听页面节点变化插件
 * 参数说明：stepNum -> 缓存的记录数量，target监听的目标节点
 * 插件说明：针对第三方jq拖拽插件dragula.js设计的插件，主要用于实现撤销与反撤销功能
 * 注意：此插件不兼容IE 10及其以下的浏览器
 */
function Undo(stepNum, target){
    if(!stepNum || typeof stepNum !== "number" || stepNum > 100){
        stepNum = 100;
    }
    if(!target || typeof target !== "string"){
        target = "#view-detail-box";
    }
    this.maxStep = stepNum; // 最大缓存步骤数
    this._undo = []; // 记录可撤销的操作
    this._redo = []; // 记录可反撤销的操作
    this._target = this.$q(target);
    this.init();
}

Undo.prototype = {
    constructor: Undo, // 将Undo对象的constructor重新指回该对象
    // 初始化操作，用于执行各种监听事件
    init: function(){
        var _self = this;
        _self.$q(".head-left-undo").addEventListener("click", function(){ _self.makeEvent("undo") }); // 撤销，ctrl+z
        _self.$q(".head-left-redo").addEventListener("click", function(){ _self.makeEvent("redo") }); // 反撤销, ctrl+y

        // 获取浏览器的MutationObserver方法对象，用于监听节点的变化与属性修改
        var MutationObserver = window.MutationObserver
            || window.WebKitMutationObserver
            || window.MozMutationObserver;
        var callback = function(records){ // 节点发生变化后的回调
            var record = records[records.length - 1];
            if(record.target.id !== "view-detail-box" && record.target.className.indexOf("gu-transit") === -1){ // 过滤节点修改与假数据
                var preContent = _self._undo[_self._undo.length - 1], curContent = _self._target.innerHTML;
                if(preContent !== curContent){ // 判断当前数据与上一次数据是否相等，防止容器内随意拖拽
                    _self.addData();
                }
            }
        }

        var mo = new MutationObserver(callback);
        var options = { // 配置项
            'childList': true, // 监听子节点变动
            'addedNodes': true, // 监听新增的子节点列表
            'removedNodes': true, // 监听移除的子节点列表
            'attributes': true, // 监听子节点属性变动
            'attributeOldValue': true, // 旧的节点属性值
            'characterData': true, // 监听文本变动
            'subtree': true
        };
        mo.observe(_self._target, options);
        _self.addData();
    },
    // 添加操作数据至缓存
    addData: function(){
        var _self = this, 
        stepNum = _self._undo.length + _self._redo.length;
        if(stepNum >= _self.maxStep){ // 注入用户新的操作时判断缓存的操作数，大于缓存数则删除可撤销区第一个
            _self._undo.shift();
        }
        var html =  _self._target.innerHTML;
        _self._undo.push(html); // 记录可撤销数据
        if(_self._undo.length > 1){
            _self.removeCls(_self.$q(".head-left-undo"), "not-allowed");
        }
    },
    /**
     * 执行撤销或反撤销操作
     * 参数说明：type -> 撤销操作类型
     * undoData：可以撤销的数据，redoData：可以反撤销的数据
     */
    makeEvent: function(type){
        var _self = this, undoData = "", redoData = "";

        if(type === "undo" && _self._undo.length > 1){ // 撤销操作
            redoData = _self._undo.pop(); // 删除可撤销的最后一个数据
            _self._redo.push(redoData); // 添加到可反撤销处
            _self._target.innerHTML = _self._undo[_self._undo.length - 1]; // 还原至上一步操作结束
            _self.removeCls(_self.$q(".head-left-redo"), "not-allowed");
            if(_self._undo.length === 1){ // 移除数据后判断是否为1
                _self.addCls(_self.$q(".head-left-undo"), "not-allowed");
            }
            return;
        }

        if(type === "redo" && _self._redo.length > 0){ // 反撤销操作
            undoData = _self._redo.pop();
            _self._undo.push(undoData); // 添加到可撤销处
            _self._target.innerHTML = undoData; // 还原至下一步操作结束
            _self.removeCls(_self.$q(".head-left-undo"), "not-allowed");
            if(_self._redo.length === 0){ // 移除数据后判断是否为0
                _self.addCls(_self.$q(".head-left-redo"), "not-allowed");
            }
        }
    },
    $q: function(selector){ // 查找元素
        if(selector.indexOf("#") > -1){
            return document.getElementById(selector.replace("#",""));
        }
        return document.querySelector(selector);
    },
    addCls: function(target, name){ // 添加样式
        var cls = target.className;
        if(cls === ""){
            target.className = name;
            return;
        }
        if(cls.indexOf(name) === -1){
            target.className = (cls + " " + name).trim();
        }
        
    },
    removeCls: function(target, name){ // 移除样式
        var cls = target.className;
        if(cls !== "" && cls.indexOf(name) > -1){
            target.className = cls.toString().replace("not-allowed", "");
        }
    }
}

new Undo();